home *** CD-ROM | disk | FTP | other *** search
- /*
- * StringArt May 1985 Ron Hitchens
- *
- * hitchens@ut-sally.ARPA ihnp4!ut-sally!hitchens.UUCP
- *
- * This program was adapted from a demo for the Sun Microsystems
- * Unix workstation
- * Ported to the Mac May 10, 1985 by Ron Hitchens
- *
- * It runs a lot faster on the Sun, even though it's also 68000 based
- * and is running multituser Unix 4.2BSD, has more layers of software
- * between you and the screen, and is computing coordinates using
- * floating point. It's clock rate is faster, and it's vector drawing
- * routines are coded for 1-bit-wide lines, but even so...
- *
- * This program is public domain. It may be be freely distributed,
- * copied and used. All I ask is that my name and this notice remain
- * on it. And that modifications be noted in this source listing and
- * returned to me if possible at the above email address.
- *
- * History:
- * May 85 Hitchens Created.
- * May 28 Hitchens Look at screen size vars, add comments
- *
- *
- * Compilation instructions with Megamax C:
- * You need the C source files stringart.c and gennums.c
- * 1. compile and link gennums.c
- * 2. run gennums, it creates a C source file named stringnums.c
- * This name is hard-coded in gennums.c
- * 3. Compile stringnums.c, but don't link it. Keep stringnums.o
- * 4. Compile stringart.c
- * 5. Link stringart.o and stringnums.o together to make stringart
- * 6. Run StringArt.
- * 7. Sit back and enjoy the show.
- * If you make changes to stringart, after doing the above, you can skip
- * to 4. Stringnums doesn't need to be re-compiled.
- * If you want to port this to another flavor of C you're on your own.
- *
- * The icon was created manually with the resource editor, to retain
- * the icon when recompiling just specify the name of the old
- * StringArt application file as the output of the linker. It will
- * overwrite the code but won't disturb the icon resources.
- */
-
- /*
- This demo creates random vector designs. This is accomplished by
- randomly choosing a function for each coordinate halve of the two
- points describing a vector that moves through two dimensional
- space. Both x coordinate halves cannot be the same since the design
- would simply be a collection of vertical lines. Similarly both
- y coordinate halves cannot be the same.
-
- The functions are:
-
- function[0][x] = sin( 2*PI*x/NUMLINES )
- function[1][x] = -sin( 2*PI*x/NUMLINES )
- function[2][x] = cos( 2*PI*x/NUMLINES )
- function[3][x] = -cos( 2*PI*x/NUMLINES )
- function[4][x] = sin( 4*PI*x/NUMLINES )
- function[5][x] = -sin( 4*PI*x/NUMLINES )
- function[6][x] = cos( 4*PI*x/NUMLINES )
- function[7][x] = -cos( 4*PI*x/NUMLINES )
- function[8][x] = sin( 6*PI*x/NUMLINES )
- function[9][x] = -sin( 6*PI*x/NUMLINES )
- function[10][x] = cos( 6*PI*x/NUMLINES )
- function[11][x] = -cos( 6*PI*x/NUMLINES )
- function[12][x] = 2 * abs( x - (NUMLINES/2) - 1 ) *not used*
-
- The values of the functions were pre-computed to have the demo
- run as fast as possible. The program runs in an endless loop, it
- will terminate if the "Quit" menu item is selected or a 'Q' is typed.
- */
-
- #include <qd.h> /* quikdraw */
- #include <qdvars.h> /* quickdraw globals */
- #include <win.h> /* window manager */
- #include <menu.h> /* menu manager */
- #include <event.h> /* event manager */
-
- #define NUMLINES 343 /* number of vectors in a design */
- #define NUM_FUNCTIONS 12 /* number of functions */
- #define VOFFSET 19 /* offset from top, drag is under menu bar */
- #define BORDER 2 /* surrounding blank border */
-
- /* get real size of screen, in case of Lisa or something new */
- #define HSIZE (qdvars.screenbits.bounds.a.right)
- #define VSIZE (qdvars.screenbits.bounds.a.bottom)
-
- windowrecord myrecord; /* if you can't figure out what these are */
- windowptr mywindow; /* you'd better give up right now */
- menuhandle mymenu;
-
- int holding = 0; /* are we currently holding? */
- int step = 0; /* single step? */
-
- int q [4][NUMLINES]; /* temp array for scaled coordinates */
- extern int p [NUM_FUNCTIONS][NUMLINES];
- /* pre-computed points, scaled by 1024 */
-
- main()
- {
- int i, j, k, l, m;
- long secs;
-
- init (); /* do housekeeping */
- showinfo (); /* display bragging info */
- getdatetime (&qdvars.randseed); /* randomize the random() function */
-
- while (1) { /* main loop, go round and round forever */
- /* pick the functions */
- while ((i = random() % NUM_FUNCTIONS) < 0) /* nothing */ ;
- while ((j = random() % NUM_FUNCTIONS) == i || (j < 0));
- while ((k = random() % NUM_FUNCTIONS) < 0);
- while ((l = random() % NUM_FUNCTIONS) == k || (l < 0));
-
- /* scalepoints was a hack to speed up the line drawing, by
- avoiding the scaling computations for each coordinate
- while the lines are being drawn. It didn't help as much
- as I'd hoped, only about a second is saved, maybe less.
- Apparently I'm pushing QuickDraw as fast as it will go.
- The same calculations using floating point took about
- 45 seconds, YOW! */
-
- scalepoints (i, k, j, l); /* scale to the window */
- while (holding && !step) /* loop here if holding */
- checkmenu (); /* watch events when holding */
-
- blankwindow (mywindow); /* clear to black or white */
- setport (mywindow); /* point QD at my window */
- /* the above statement shouldn't be necessary, but
- something sneaky, like the screensaver DA, could
- be lurking in the darkness and may change the
- grafport out from under us. Grrr... */
- obscurecursor (); /* get rid of the cursor */
-
- for (m = 0; m < NUMLINES; m++) { /* draw the vectors */
- moveto (q [0][m], q [1][m]);
- lineto (q [2][m], q [3][m]);
- }
-
- systemtask (); /* in case of DAs */
- if (!holding) sleep (6); /* pause for ~6 secs */
- step = 0; /* only good for one shot */
- }
- }
-
-
- /* the numbers in the array p were pre-computed using floating point
- arithmetic, they were all sin or cos functions and so yielded
- numbers [-1, 1]. These numbers were then scaled up by 1024 so they
- could be stored as integers. 1024 was used so they could be scaled down
- again with a shift-right-10 rather than a divide operation. I use two
- statements to compute each number to be sure the intermediate values
- are calculated as longs, the values involved far exceed the magnitude
- possible in a 16-bit int. */
-
- scalepoints (x1, y1, x2, y2) int x1, y1, x2, y2;
- {
- long l, h, v;
- int i;
-
- h = (HSIZE) / 2;
- v = (VSIZE - VOFFSET) / 2;
- for (i = 0; i < NUMLINES; i++) {
- l = (((long)p [x1][i] * (h - BORDER)) >> 10) + h;
- q [0][i] = (int)l;
- l = (((long)p [y1][i] * (v - BORDER)) >> 10) + v;
- q [1][i] = (int)l;
- l = (((long)p [x2][i] * (h - BORDER)) >> 10) + h;
- q [2][i] = (int)l;
- l = (((long)p [y2][i] * (v - BORDER)) >> 10) + v;
- q [3][i] = (int)l;
- checkmenu (); /* see if there are any events to be handled */
- }
- }
-
-
- checkmenu ()
- /* test for and handle any events, such as keyboard and mouse */
- {
- eventrecord theevent;
- windowptr tmpptr;
-
- /* loop as long as there are events in the queue */
- while (getnextevent (everyevent, &theevent)) {
- switch (theevent.what) {
- case abortevt: /* something is telling us to stop */
- exit (0);
- case keydown: /* got a key */
- /* does this key belong to a menu? */
- if (menukey ((char) (theevent.message % 256))) {
- /* yes */
- do_menu (menukey ((char) (theevent.message % 256)));
- } else {
- step = 1; /* step once (undoc feature) */
- return;
- }
- break;
- case mousedown: /* mouse clicked */
- if (findwindow(&theevent.where, &tmpptr) == inmenubar){
- do_menu (menuselect (&theevent.where));
- } else {
- step = 1; /* cycle once */
- }
- break;
- case updateevt: /* always get one of these at startup */
- beginupdate (mywindow);
- endupdate (mywindow); /* only way to get rid of it */
- break;
- case nullevent: /* shouldn't happen, filtered above */
- return;
- default:
- break; /* not interested in anything else */
- }
- }
- }
-
- do_menu (item) long item; /* handle a menu selection */
- {
- hilitemenu (1);
- switch (item) {
- case 0x00010001: /* menu 1, item 1 (hold) */
- toggle_hold ();
- break;
- case 0x00010002: /* menu 1, item 2 (invert) */
- invert ();
- break;
- case 0x00010003: /* menu 1, item 3 (author info) */
- showinfo ();
- break;
- case 0x00010004: /* menu1, item 4 (quit) */
- exit (0);
- }
- hilitemenu (0);
- }
-
-
- toggle_hold ()
- {
- holding = !holding; /* toggle the flag */
- checkitem (mymenu, 1, holding);
- /* place or remove checkmark, depending on holding */
- }
-
- invert ()
- {
- setport (mywindow); /* on the off chance... */
- if (mywindow->pnmode == patbic) {
- penmode (patcopy); /* draw black */
- textmode (srccopy);
- } else {
- penmode (patbic); /* draw white */
- textmode (srcbic);
- }
- invertrect (&mywindow->portrect); /* flip what's onscreen */
- }
-
-
- init () /* do initial housekeeping chores */
- {
- managerinits ();
- windowinits ();
- menuinits ();
- }
-
- managerinits()
- {
- initgraf(&theport); /* initialize qd (theport in qdvars) */
- initfonts(); /* initialize font manager */
- initwindows(); /* initialize window manager */
- initmenus(); /* initialize menus */
- initcursor(); /* set cursor to arrow */
- flushevents(everyevent, 0); /* lose any outstanding events */
- }
-
- windowinits()
- {
- rect temprect;
-
- setrect(&temprect, 0, VOFFSET, HSIZE, VSIZE);
- mywindow = newwindow(&myrecord, &temprect, "Stringart", 1, 0,
- (long) -1, 0, (long) 0);
- setport(mywindow); /* draw in my window */
- selectwindow(mywindow); /* make mine active */
- pennormal (); /* make sure pen has defaults */
- penmode (patbic); /* make it draw white */
- textmode (srcbic); /* make text draw white */
- textfont (0); /* use system font */
- textface ((short) bold); /* use a fancy text style */
- }
-
- menuinits ()
- {
- mymenu = newmenu (1, "StringArt"); /* allocate a menu struct */
- appendmenu (mymenu, "Hold/H"); /* load the item values */
- appendmenu (mymenu, "Invert/I");
- appendmenu (mymenu, "Author Info/A");
- appendmenu (mymenu, "Quit/Q");
- insertmenu (mymenu, 0); /* let the OS know about it */
- drawmenubar (); /* make it show up */
- }
-
- sleep (s) int s;
- /* sleep for approx s seconds, could vary from s-1 to s seconds */
- {
- long s1, s2;
-
- getdatetime (&s1); /* what time is it now? */
- s1 += s; /* looking for now + s seconds */
- while (1) {
- getdatetime (&s2);
- if (s1 <= s2) break; /* ok, the time has come */
- checkmenu (); /* keep an eye on events around you */
- }
- }
-
- blankwindow (w) windowptr w;
- {
- if (w->pnmode == patbic) { /* drawing white? */
- fillrect (&w->portrect, qdvars.black); /* yes, paint window black */
- } else {
- fillrect (&w->portrect, qdvars.white); /* no, paint it white */
- }
- }
-
- showinfo () /* display bragging info */
- {
- static char *s [] = {
- "StringArt by Ron Hitchens",
- "hitchens@ut-sally.ARPA or ...!ihnp4!ut-sally!hitchens.UUCP",
- "", "Written in MegaMax C",
- "This program is public domain", "",
- "While in Hold, click mouse to single step",
- "To save an image, type: SHIFT/CMD/3" ,
- "To print an image, type: SHIFT/CMD/4" };
- int i, n;
- static int busy = 0;
-
- hilitemenu (1); /* turn on the menu */
- if (busy) return; /* info screen already up */
- busy = 1; /* to prevent recursive displays */
- setport (mywindow); /* just in case */
- obscurecursor (); /* get rid of the cursor */
- blankwindow (mywindow);
- n = sizeof (s) / sizeof (s [0]); /* how many lines of text? */
- textsize (24); /* get fancy for first line */
- for (i = 0; i < n; i++) { /* display all the text */
- moveto ((HSIZE / 2) - (stringwidth (s [i]) / 2),
- (VSIZE / (n + 1)) + (i * (VSIZE / (n + 1))));
- drawstring (s [i]); /* draw one of the text lines */
- textsize (12); /* back to normal for the rest */
- }
- if (!holding) sleep (10); /* wait a bit */
- hilitemenu (0); /* turn off the menu */
- busy = 0; /* ok, I'm finished */
- }
-